﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using System.Diagnostics;

namespace DarkNeuron.Services.Input
{
	public abstract class Button
	{
		public bool Enabled { get; set; }
		public ButtonManager ButtonManager { get; private set; }
		protected string name = String.Empty;
		public string Name { get { return name; } protected set { ChangeName(value); } }
		// States
		public bool IsPressed { get; protected set; }
		public bool IsReleased { get; protected set; }
		public bool IsHeld { get; protected set; }
		public bool IsUp { get; protected set; }
		public bool IsDown { get; protected set; }
		protected ButtonDevice device = ButtonDevice.Keyboard;
		public ButtonDevice Device { get { return device; } }
		protected ButtonState currentButtonState = ButtonState.Up;
		protected ButtonState previousButtonState = ButtonState.Up;
		// Events
		public event ButtonEventHandle Pressed;
        public event ButtonEventHandle Released;
		public event ButtonEventHandle DoubleTap;
		public event ButtonEventHandle DoubleRelease;
		// Double tap and double release counters and variables
		protected TimeSpan doubleTimeSpan = new TimeSpan(0, 0, 0, 0, 750);
		protected bool isDoubleTap;
		protected bool isDoubleRelease;
		protected TimeSpan currentTimeSpanTap = new TimeSpan(0);
		protected TimeSpan currentTimeSpanRelease = new TimeSpan(0);


		public Button(string name, ButtonDevice device)
		{
			Console.WriteLine("Button|Information:> New Button \"{0}\" is being initialized...", name);

			bool pass = true;
			Enabled = true;
			ButtonManager = GameInput.ButtonManager;
			this.device = device;
			// If name was not given then we will generate it automaticly
			if (String.IsNullOrEmpty(name))
			{
				Console.WriteLine("Button|Warning:> name typeof(string) will be generated automatily!");
				pass = ChangeName(ref name, true);
			}
			pass = ChangeName(name);

			if (pass) Console.WriteLine("Button|Information:> Button \"{0}\" is successfully initialized.", name);
			else Console.WriteLine("Button|Error:> Button \"{0}\" is NOT successfully initialized!", name);
		}


		public virtual void Update(GameTime gameTime)
		{
			if (Enabled)
			{
				CheckDouble(gameTime);
				ChangeStatus();				
			}
		}
		private void CheckDouble(GameTime gameTime)
		{
			// Checks thedouble tap and release options
			if (IsPressed && DoubleTap != null)
			{
				if (!(isDoubleTap = !isDoubleTap))
				{
					currentTimeSpanTap = new TimeSpan(0);
					DoubleTap(device, ButtonState.Down);
				}
			}
			else if (IsReleased && DoubleRelease != null)
			{
				if (!(isDoubleRelease = !isDoubleRelease))
				{
					currentTimeSpanRelease = new TimeSpan(0);
					DoubleRelease(device, ButtonState.Up);
				}
			}
			else
			{
				// Adds time from last frame to this frame and adds it to time span
				if (isDoubleTap)
				{
					currentTimeSpanTap += gameTime.ElapsedGameTime;
					// If current time passed the default time then double tap is disabled
					if (currentTimeSpanTap > doubleTimeSpan)
					{
						Console.WriteLine("Ugaseno zbog vremena...");
						isDoubleTap = false;
						currentTimeSpanTap = new TimeSpan(0);
					}
				}
				if (isDoubleRelease)
				{
					currentTimeSpanRelease += gameTime.ElapsedGameTime;
					// If current time passed the default time then double release is disabled
					if (currentTimeSpanRelease > doubleTimeSpan)
					{
						isDoubleRelease = false;
						currentTimeSpanRelease = new TimeSpan(0);
					}
				}
			}
		}
		private void ChangeStatus()
		{
			if (currentButtonState == ButtonState.Down) // Key DOWN
			{
				IsUp = false;
				IsDown = true;

				if (previousButtonState == ButtonState.Down) // Now UP before DOWN
				{
					IsHeld = true;

					IsPressed = false;
					IsReleased = false;
				}
				else // Now DOWN before UP
				{
					IsPressed = true;

					IsHeld = false;
					IsReleased = false;

					if (Pressed != null)
						Pressed(device, ButtonState.Down);
				}
			}
			else // Key UP
			{
				IsUp = true;
				IsDown = false;

				if (previousButtonState == ButtonState.Down) // Now UP before DOWN
				{
					IsReleased = true;

					IsPressed = false;
					IsHeld = false;

					if (Released != null)
						Released(device, ButtonState.Up);
				}
				else // Now Up before Up
				{
					// Everithing is false bacause nothing happened
					IsPressed = false;
					IsReleased = false;
					IsHeld = false;
				}
			}
		}

		public virtual bool ChangeName(string newName)
		{
			// Error handle
			if (String.IsNullOrEmpty(newName)) Console.WriteLine("Button|Error:> newName typeof(string) can't be empty!");
			Debug.Assert(!String.IsNullOrEmpty(newName), "newName is null or empty.");
			// END of Error handle


			// Checks that new name dont already exists 
			if (!ButtonManager.CheckExistingName(newName))
			{
				Console.WriteLine("Button|Information:> Button name changed from \"{0}\" to \"{1}\".", name, newName);
				name = newName;
				return true;
			}
			else
			{
				Console.WriteLine("Button|Warning:> newName typeof(string) already exists in buttonManager's list.");
				return false;
			}
		}
		private bool ChangeName(ref string toName, bool autoGenerate)
		{
			// If auto-generation is allowed
			if (autoGenerate)
			{
				string newName = String.Empty;
				bool foundNewName = false;
				int numberCouter = 1;
				// Goes throught the loop until we find correct name for button
				do
				{
					newName = String.Format("button{0}{1}", device.ToString(), numberCouter);
					if (!ButtonManager.CheckExistingName(newName))
						break;
					else numberCouter++;
				} while (!foundNewName);

				toName = newName;
			} // ... else this will change name in regular way
			else return ChangeName(toName);

			return true;
		}
	}
}
